﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using K9Nano.Dependency;
using K9Nano.Domain;
using K9Nano.Remoting;
using K9Nano.Share.Extensions;
using Microsoft.Extensions.Logging;

namespace K9Nano.Authorization
{
    public class DefaultRoleResourceStore : K9DomainServiceBase, IRoleResourceStore, IScopedDependency
    {
        private readonly IRepository<RoleResource> _repository;

        public DefaultRoleResourceStore(IUnitOfWork uow, ILoggerFactory loggerFactory) : base(uow, loggerFactory)
        {
            _repository = uow.Repository<RoleResource>();
        }

        public async ValueTask<IReadOnlyList<string>> GetResourcesAsync(string api, IEnumerable<string> roles, CancellationToken cancellation)
        {
            var array = roles.AsArray();
            var res = await _repository.GetAllListAsync(
                x => x.Api == api && array.Contains(x.Role), cancellation);
            return res.Select(x => x.Resource).ToList();
        }

        public async ValueTask<IReadOnlyList<string>> GetRolesInResourceAsync(string api, string resource, CancellationToken cancellation)
        {
            var res = await _repository.GetAllListAsync(
                x => x.Api == api && x.Resource == resource, cancellation);
            return res.Select(x => x.Role).ToList();
        }

        public async ValueTask AddRoleToResourcesAsync(string api, string role, IEnumerable<string> resources,
            CancellationToken cancellation)
        {
            var exited = await _repository.GetAllListAsync(x => x.Api == api
                                                                && x.Role == role, cancellation);
            var added = resources
                .Where(x => exited.All(e => e.Resource != x))
                .Select(x => new RoleResource
                {
                    Api = api,
                    Role = role,
                    Resource = x
                })
                .ToList();
            await _repository.AddRangeAsync(added, cancellation);
        }

        public async ValueTask RemoveRoleFromResourcesAsync(string api, string resource, IEnumerable<string> roles,
            CancellationToken cancellation)
        {
            var array = roles.AsArray();
            await _repository.DeleteAsync(x => x.Api == api
                                             && x.Resource == resource
                                             && array.Contains(x.Role),
                soft: false,
                cancellation: cancellation);
        }
    }
}